package vmcs.store;

// Copyright 1997 ISS.
// The contents contained in this document may not be reproduced in any
// form or by any means, without the written permission of ISS, other
// than for the purpose for which it has been supplied.
//
// Author: Olivo Miotto
//

import java.io.IOException;
import java.util.ArrayList;
import java.util.Observer;

import vmcs.common.Environment;
import vmcs.common.VMCSException;
import vmcs.controller.MainController;
import vmcs.controller.MaintenanceController;
import vmcs.machinery.Door;

public class StoreController {

	private CashStore cStore;
	private DrinksStore dStore;


	private Door door;
	private CashData cashItem;
	private DrinkData drinkItem;
	private ArrayList<Observer> listOfObservers;

	public MainController mainCtrl;
	
	public StoreController(MainController mctrl) {
		mainCtrl = mctrl;
	}

	public void initialize() throws VMCSException {
		try {
			door = new Door();
			cStore = CashStore.getInstance();
			dStore = DrinksStore.getInstance();
			listOfObservers=new ArrayList<Observer>();
			initializeStores();
		} catch (VMCSException e) {
			throw new VMCSException("StoreController.initialize",
					"Error in initialize store controller" + e);
		}
	}

	public MainController getMainController() {
		return mainCtrl;
	}
	
	public ArrayList<Observer> getListOfObservers() {
		return listOfObservers;
	}
	
	public void initializeStores() throws VMCSException {
		try {
			listOfObservers.add(mainCtrl.getMachineryController());
			listOfObservers.add(mainCtrl.getTransactionController());
			listOfObservers.add(mainCtrl.getMaintenanceController());
			initializeCashStore();
			initializeDrinkStore();
		} catch (VMCSException e) {
			throw new VMCSException("StoreController.initializeStores",
					"Error in initialize cash store" + e);
		}
	}

	public void initializeDrinkStore() throws VMCSException {

		try {
			// get the drink file from the environment property file;
			drinkItem = new DrinkData();
			int numOfItems;

			numOfItems = drinkItem.getNumOfItems();
			dStore.setStoreSize(numOfItems);
			int i;
			StoreItem sItem;


			for (i = 0; i < numOfItems; i++) {
				sItem = drinkItem.getItem(i + 1);
				for(Observer observer:listOfObservers){
					sItem.addObserver(observer);
				}

				// add drink item in this function;
				addDrinksItem(i, sItem);
			}

		} catch (Exception e) {
			throw new VMCSException("StoreController.initializeDrinkStore",
					"Error in initialize drink store " + e);
		}
	}

	public void initializeCashStore() throws VMCSException {
		try {
			// get the cash file from the environment property file;
			cashItem=new CashData();			
			int numOfItems;
			numOfItems = cashItem.getNumOfItems();
			cStore.setStoreSize(numOfItems);
			int i;

			StoreItem sItem;
			for (i = 0; i < numOfItems; i++) {
				sItem = cashItem.getItem(i + 1);
				for(Observer observer:listOfObservers){
					sItem.addObserver(observer);
				}
				// add cash item in this function;
				cStore.addItem(i,sItem);
			}
		} catch (Exception e) {
			throw new VMCSException("StoreController.initializeCashStore",
					"Error in initialize cash store " + e);
		}
	}

	public void addDrinksItem(int idx, StoreItem item) throws VMCSException {
		StoreObject ob; // existed object
		StoreObject ob1; // the new object

		ob1 = item.getContent();
		ob = dStore.findObject(ob1.getName());

		// check for the consistency of brand price;
		if (ob != null) {
			DrinksBrand bd;
			DrinksBrand bd1;
			bd = (DrinksBrand) ob;
			bd1 = (DrinksBrand) ob1;
			if (bd.getPrice() != bd1.getPrice()) {
				// the new price is not the same as that of the prievous item
				// with the same name;
				throw new VMCSException("StoreController.addDrinksItem",
						"Inconsist brand price");
			}
		}
		if (ob == null) {
			dStore.addItem(idx, (StoreItem) item);
			return;
		}

		item.setContent(ob);

		dStore.addItem(idx, item);
	}

	/*public void changeStoreQty(char type, int idx, int qty) {
		try {
			System.out.println("StoreController.changeStoreQty: type:" + type
					+ "   qty:" + qty);
			if (type == Store.CASH)
				cStore.setQuantity(idx, qty);
			else
				dStore.setQuantity(idx, qty);
			// to automatically change the displayed stock quantitiy.
			// not required in the requirement.
			MaintenanceController mctrl;
			mctrl = mainCtrl.getMaintenanceController();
			mctrl.changeStoreQty(type, idx, qty);

		} catch (VMCSException e) {
			System.out.println("StoreController.changeStoreQty:" + e);
		}
	}
*/
	
	public StoreItem getStoreItem(char type, int idx) throws VMCSException {
		if (type == Store.CASH)
			return cStore.getStoreItem(idx);
		else
			return dStore.getStoreItem(idx);
	}

	public boolean isDoorClosed() {
		return door.isDoorClosed();
	}

	public void saveCashProperty() throws VMCSException {
		int size;
		size = cStore.getStoreSize();
		int i;
		cashItem.setNumOfItems(size);
		for (i = 0; i < size; i++) {
			cashItem.setItem(i, (CashStoreItem) cStore.getStoreItem(i));
		}
		cashItem.save();
	}

	/**
	 * save the drink property when simulation is ended.
	 */
	public void saveDrinksProperty() throws VMCSException {
		int size;

		size = dStore.getStoreSize();
		drinkItem.setNumOfItems(size);

		int i;
		for (i = 0; i < size; i++) {
			drinkItem.setItem(i, (DrinksStoreItem) dStore.getStoreItem(i));
		}

		drinkItem.save();
	}

	public Store getStore(char type) {
		if (type == Store.CASH)
			return (Store) cStore;
		else
			return (Store) dStore;
	}

	/**
	 * This operation will instruct the Cash Store to store the Coin sent as
	 * input, and the update the display on the Machinery Simulator Panel.
	 * 
	 * @param c
	 *            the coin object that is inserted into the machine.
	 * @throws VMCSException
	 */
	public void storeCoin(Coin c) throws VMCSException {
		int idx = cStore.findCashStoreIndex(c);
		CashStoreItem item;
		item = (CashStoreItem) this.getStoreItem(Store.CASH, idx);
		item.store();
	}

	/**
	 * Retrieves the store size of either the Cash Store or Drinks Store.
	 * 
	 * @param type
	 *            the type of store, either Cash Store or Drinks Store.
	 * @return returns the size of the store.
	 */
	public int getStoreSize(int type) {
		if (type == Store.CASH)
			return cStore.getStoreSize();
		else
			return dStore.getStoreSize();
	}

	/**
	 * This operation retrieves the Store items in the store.
	 * 
	 * @param type
	 *            the type of store - either Cash Store or Drinks Store.
	 * @return returns an array list of Store items.
	 */
	public StoreItem[] getStoreItems(int type) {
		if (type == Store.CASH)
			return cStore.getItems();
		else
			return dStore.getItems();
	}

	/**
	 * This operation will either: - instruct the Cash Store to update the
	 * quantity of a coin denomination to the new value supplied and update the
	 * total cash held in the Cash Store; or - instruct the Drinks Store to
	 * update the drinks stock for a drinks brand requested to a new value
	 * supplied.
	 * 
	 * @param type
	 *            the type of Store - either Cash Store or Drinks Store.
	 * @param idx
	 *            the denomination of the coin, or the brand of the drink.
	 * @param qty
	 *            the quantity of the store item that needs to be changed.
	 * @throws VMCSException
	 */
	public void changeStoreQty(int type, int idx, int qty) throws VMCSException {
		System.out.println("StoreController.changeStoreQty: type:" + type
				+ "   qty:" + qty);
		if (type == Store.CASH)
			cStore.setQuantity(idx, qty);
		else
			dStore.setQuantity(idx, qty);
	}

	/**
	 * This operation retrieves either the Cash Store item or the Drinks Store
	 * item.
	 * 
	 * @param type
	 *            the type of store - either Cash Store or Drinks Store.
	 * @param idx
	 *            the index of the Cash Store or Drinks Store item.
	 * @return returns the Store item - either a Cash Store item or Drinks Store
	 *         item.
	 * @throws VMCSException
	 */
	public StoreItem getStoreItem(int type, int idx) throws VMCSException {
		if (type == Store.CASH)
			return cStore.getStoreItem(idx);
		else
			return dStore.getStoreItem(idx);
	}

	/**
	 * This operation will be used to set the price of a drink brand as
	 * determined by the Maintainer.
	 * 
	 * @param idx
	 *            the index that represents a particular brand of drinks.
	 * @param pr
	 *            the price of a particular drink brand that has to be set.
	 * @throws VMCSException
	 */
	public void setPrice(int idx, int pr) throws VMCSException {
		DrinksStoreItem item;

		item = (DrinksStoreItem) dStore.getStoreItem(idx);
		DrinksBrand bd;

		bd = (DrinksBrand) item.getContent();

		bd.setPrice(pr);
	}

	/**
	 * This operation returns the total cash held in the Cash Store.
	 * 
	 * @return returns the total cash held in the vending machine.
	 * @throws VMCSException
	 */
	public int getTotalCash() throws VMCSException {
		int i;
		int size;

		size = cStore.getStoreSize();
		CashStoreItem item;
		int qty;
		int val;
		int tc = 0;
		Coin c;

		for (i = 0; i < size; i++) {
			item = (CashStoreItem) cStore.getStoreItem(i);
			qty = item.getQuantity();
			c = (Coin) item.getContent();
			val = c.getValue();
			tc = tc + qty * val;
		}
		return tc;
	}

	/**
	 * This operation is to facilitate the transfer of all cash in Cash Store to
	 * the maintainer.
	 * 
	 * @return returns the total quantity of coins.
	 * @throws VMCSException
	 */
	public int transferAll() throws VMCSException {
		int i;
		int cc = 0; // coin quantity;
		int size = cStore.getStoreSize();

		CashStoreItem item;
		for (i = 0; i < size; i++) {
			item = (CashStoreItem) cStore.getStoreItem(i);
			cc = cc + item.getQuantity();
			item.setQuantity(0);
		}

		return cc;
	}

	/**
	 * This operation will close down the store management function of the
	 * vending machine. This involves saving the attributes of the stores to the
	 * property files by calling the saveCashProperties method and the
	 * saveDrinksProperties method.
	 * 
	 * @throws java.io.IOException
	 */
	public void closeDown() throws VMCSException {
		// save back cash property;
		saveCashProperty();
		saveDrinksProperty();
	}

	/**
	 * This operation instructs the the Drinks Store to dispense one drink, and
	 * then updates the Machinery Simulator Panel. It returns TRUE or FALSE to
	 * indicate whether dispensing was successful.
	 * 
	 * @param idx
	 *            the index that represents a particular drink brand.
	 */

	public boolean dispenseDrink(int selectedBrandIdx) {
		try {
			StoreItem storeItem = dStore.getStoreItem(selectedBrandIdx);
			storeItem.decrement();
		} catch (VMCSException e) {
			e.printStackTrace();
		}

		return true;

	}

	/**
	 * This operation returns a store of a specified type - either Cash or
	 * Drinks).
	 * 
	 * @param type
	 *            the type of store - either Cash Store or Drinks Store
	 * @return returns the store of either the Cash or Drinks type.
	 */
	public Store getStore(int type) {
		if (type == Store.CASH)
			return (Store) cStore;
		else
			return (Store) dStore;
	}

	/**
	 * This operation instructs the Cash Store to issue a number of coins of a
	 * specific denomination, and then updates the Machinery Simulator Panel. It
	 * returns TRUE or FALSE to indicate whether the change issue was
	 * successful.
	 * 
	 * @param idx
	 *            the index that represents the denomination of coin.
	 * @param numOfCoins
	 *            the number of coins of a particular denomination that has to
	 *            be returned as change.
	 * @throws VMCSException
	 */

	public void giveChange(int idx, int numOfCoins) throws VMCSException {
		CashStoreItem item;
		item = (CashStoreItem) getStoreItem(Store.CASH, idx);
		for (int i = 0; i < numOfCoins; i++)
			item.decrement();
	}

	
}
